/* 
 * 	This file is part of VPN Monitor Project 7..

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package monitor.traffic;

import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;

import monitor.interfaces.BufferDataPackage;
import monitor.interfaces.TrafficBufferPackage;
import monitor.interfaces.EnumTypes.Types;

/**
 * Represent a traffic buffer for a specific type in the enum class
 * Types. Besides the traffic buffer does this class also keep track
 * of the largest value in the buffer.
 */
class TrafficBuffer implements TrafficBufferPackage{
	private static final int DATA_BUFFER = 200;
	private static int sampleSize;
	private LinkedList<Long> tmp = null;
	private LinkedList<DataPackage> dataBuffer = null;
	private final Types type;
	private long largest = 0;
	private int totalAdded = 0, largestAdded = 0; 
	private boolean added = false;
	
	/**
	 * Constructor for TrafficBuffer, creates a new instance
	 * for a specific type in the enum class Types. The sample size
	 * specify how many values should be recorded before the mean value
	 * of these values are added to the buffer.
	 * 
	 * @param type What type that this object represent.
	 * @param sampleSize Size of the sample, value above or equal to 1.
	 */
	TrafficBuffer(Types type, int sampleSize){
		this.type = type;
		TrafficBuffer.sampleSize = sampleSize;
		dataBuffer = new LinkedList<DataPackage>();
		tmp = new LinkedList<Long>();
	}
	
	@Override
	public Types getType(){
		return type;
	}
	
	@Override
	public long getLargest(){
		return largest;
	}
	
	@Override
	public BufferDataPackage[] getBuffer(){
		DataPackage[] array = new DataPackage[dataBuffer.size()];
		return dataBuffer.toArray(array);
	}
	
	/**
	 * Change how many times traffic should be collected before
	 * adding a new value to the buffer. The value added is the 
	 * mean value for the traffic collected.
	 * @param sample A value greater then zero.
	 */
	static void changeSample(int sample){
		sampleSize = sample;
	}
	
	/**
	 * Have a new value been added to the buffer.
	 * @return New value?
	 */
	boolean added(){
		return added;
	}
	
	/**
	 * Stores the value in temporary list if the list's size equals sample size, will a new
	 * value be added to the buffer. The value added is the median value of
	 * all values in the list, all values in the temporary list is then removed.
	 * @param increase Number of bytes since last call.
	 */
	void updateTraffic(long increase){		
		added = false;
		tmp.add(increase);
		if(tmp.size() >= sampleSize){
			totalAdded++;
			added = true;
			Iterator<Long> it = tmp.iterator();
			long accumilative = 0;
			while(it.hasNext())
				accumilative += it.next();
			tmp.clear();
			long value = accumilative/sampleSize;
			if(totalAdded - largestAdded > DATA_BUFFER)
				calculateLargest();
			if(value > largest){
				largest = value;
				largestAdded = totalAdded;
			}
			if(dataBuffer.size() <= DATA_BUFFER)
				dataBuffer.add(new DataPackage(value));
			else {
				dataBuffer.remove();
				dataBuffer.add(new DataPackage(value));
			}
		}
	}
	
	/**
	 * Sets largest to zero and then find the largest
	 * value in the buffer and update largestAdded.
	 */
	private void calculateLargest(){
		largest = 0;
		for(int i=0;i<dataBuffer.size();i++)
			if(dataBuffer.get(i).value > largest){
				largest = dataBuffer.get(i).value;
				largestAdded = totalAdded - dataBuffer.size() + i + 1;
			}
	}
	
	/**
	 * Data package for the traffic buffer, contains a value for
	 * a specific time, that is also stored.
	 */
	private class DataPackage implements BufferDataPackage{
		private final long value;
		private final int[] time;
		
		/**
		 * Constructor for DataPackage, creates a time stamp when
		 * a new object is created in the format {HH,MM}. 
		 * @param value Traffic value.
		 */
		private DataPackage(long value){
			this.value = value;
			time = new int[2];
			time[0] = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
			time[1] = Calendar.getInstance().get(Calendar.MINUTE);
		}

		@Override
		public int[] getTime() {
			return time;
		}

		@Override
		public long getValue() {
			return value;
		}
	}
}
